{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Not Just a Context Manager"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Just because our class implements the context manager protocol does not mean it cannot do other things as well!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In fact the `open` function we use to open files can be used with or without a context manager:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "f = open('test.txt', 'w')\n",
    "f.writelines('this is a test')\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we did not use a context manager - the `open` function simply returned the file object - but we had to close the file ourselves - there was not context used.\n",
    "\n",
    "On the other hand we can also use it with a context manager:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['this is a test']\n"
     ]
    }
   ],
   "source": [
    "with open('test.txt') as f:\n",
    "    print(f.readlines())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can implement classes that implement their own functionality as well as a context manager if we want to."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class DataIterator:\n",
    "    def __init__(self, fname):\n",
    "        self._fname = fname\n",
    "        self._f = None\n",
    "    \n",
    "    def __iter__(self):\n",
    "        return self\n",
    "    \n",
    "    def __next__(self):\n",
    "        row = next(self._f)\n",
    "        return row.strip('\\n').split(',')\n",
    "    \n",
    "    def __enter__(self):\n",
    "        self._f = open(self._fname)\n",
    "        return self\n",
    "    \n",
    "    def __exit__(self, exc_type, exc_value, exc_tb):\n",
    "        if not self._f.closed:\n",
    "            self._f.close()\n",
    "        return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['Summons Number', 'Plate ID', 'Registration State', 'Plate Type', 'Issue Date', 'Violation Code', 'Vehicle Body Type', 'Vehicle Make', 'Violation Description']\n",
      "['4006478550', 'VAD7274', 'VA', 'PAS', '10/5/2016', '5', '4D', 'BMW', 'BUS LANE VIOLATION']\n",
      "['4006462396', '22834JK', 'NY', 'COM', '9/30/2016', '5', 'VAN', 'CHEVR', 'BUS LANE VIOLATION']\n",
      "['4007117810', '21791MG', 'NY', 'COM', '4/10/2017', '5', 'VAN', 'DODGE', 'BUS LANE VIOLATION']\n",
      "['4006265037', 'FZX9232', 'NY', 'PAS', '8/23/2016', '5', 'SUBN', 'FORD', 'BUS LANE VIOLATION']\n",
      "['4006535600', 'N203399C', 'NY', 'OMT', '10/19/2016', '5', 'SUBN', 'FORD', 'BUS LANE VIOLATION']\n",
      "['4007156700', '92163MG', 'NY', 'COM', '4/13/2017', '5', 'VAN', 'FRUEH', 'BUS LANE VIOLATION']\n",
      "['4006687989', 'MIQ600', 'SC', 'PAS', '11/21/2016', '5', 'VN', 'HONDA', 'BUS LANE VIOLATION']\n",
      "['4006943052', '2AE3984', 'MD', 'PAS', '2/1/2017', '5', 'SW', 'LINCO', 'BUS LANE VIOLATION']\n",
      "['4007306795', 'HLG4926', 'NY', 'PAS', '5/30/2017', '5', 'SUBN', 'TOYOT', 'BUS LANE VIOLATION']\n",
      "['4007124590', 'T715907C', 'NY', 'OMT', '4/3/2017', '5', 'SUBN', 'TOYOT', 'BUS LANE VIOLATION']\n",
      "['5096061966', 'HRC9475', 'NY', 'PAS', '4/18/2017', '7', 'SUBN', 'CADIL', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5094070400', 'DYP8042', 'NY', 'PAS', '10/26/2016', '7', 'SUBN', 'CHEVR', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5094906770', 'G30ESY', 'NJ', 'PAS', '1/1/2017', '7', 'WAGO', 'CHRYS', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5093319363', 'GGT8868', 'NY', 'PAS', '9/6/2016', '7', 'SUBN', 'CHRYS', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5092638849', '107DDD', 'NY', 'SPO', '7/20/2016', '7', 'SUBN', 'DODGE', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5093455337', 'ENQT55', 'FL', 'PAS', '9/12/2016', '7', 'VN', 'DODGE', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5094961366', 'GSU4156', 'OH', 'PAS', '1/7/2017', '7', '4S', 'DODGE', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5092906534', 'V84FXV', 'NJ', 'PAS', '8/7/2016', '7', 'WAGO', 'FIR', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5093620865', 'AD80228', 'AZ', 'PAS', '9/24/2016', '7', 'TK', 'FORD', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5092733548', 'EVX4041', 'NY', 'PAS', '7/26/2016', '7', 'SUBN', 'FORD', 'FAILURE TO STOP AT RED LIGHT']\n"
     ]
    }
   ],
   "source": [
    "with DataIterator('nyc_parking_tickets_extract.csv') as data:\n",
    "    for row in data:\n",
    "        print(row)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Of course, we cannot use this iterator without also using the context manager since the file would not be opened otherwise:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "data = DataIterator('nyc_parking_tickets_extract.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'NoneType' object is not an iterator",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-6-5c7f892187c8>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mfor\u001b[0m \u001b[0mrow\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mdata\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      2\u001b[0m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mrow\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m<ipython-input-2-07bd91c8253c>\u001b[0m in \u001b[0;36m__next__\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m      8\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      9\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__next__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 10\u001b[1;33m         \u001b[0mrow\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_f\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     11\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[0mrow\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mstrip\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'\\n'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m','\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     12\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: 'NoneType' object is not an iterator"
     ]
    }
   ],
   "source": [
    "for row in data:\n",
    "    print(row)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But I want to point out that creating the context manager and using the `with` statement can be done in two steps if we want to:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "data_iter = DataIterator('nyc_parking_tickets_extract.csv')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "At this stage, the object has been created, but the `__enter__` method has not been called yet.\n",
    "\n",
    "Once we use `with`, then the file will be opened, and the iterator will be ready for use:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['Summons Number', 'Plate ID', 'Registration State', 'Plate Type', 'Issue Date', 'Violation Code', 'Vehicle Body Type', 'Vehicle Make', 'Violation Description']\n",
      "['4006478550', 'VAD7274', 'VA', 'PAS', '10/5/2016', '5', '4D', 'BMW', 'BUS LANE VIOLATION']\n",
      "['4006462396', '22834JK', 'NY', 'COM', '9/30/2016', '5', 'VAN', 'CHEVR', 'BUS LANE VIOLATION']\n",
      "['4007117810', '21791MG', 'NY', 'COM', '4/10/2017', '5', 'VAN', 'DODGE', 'BUS LANE VIOLATION']\n",
      "['4006265037', 'FZX9232', 'NY', 'PAS', '8/23/2016', '5', 'SUBN', 'FORD', 'BUS LANE VIOLATION']\n",
      "['4006535600', 'N203399C', 'NY', 'OMT', '10/19/2016', '5', 'SUBN', 'FORD', 'BUS LANE VIOLATION']\n",
      "['4007156700', '92163MG', 'NY', 'COM', '4/13/2017', '5', 'VAN', 'FRUEH', 'BUS LANE VIOLATION']\n",
      "['4006687989', 'MIQ600', 'SC', 'PAS', '11/21/2016', '5', 'VN', 'HONDA', 'BUS LANE VIOLATION']\n",
      "['4006943052', '2AE3984', 'MD', 'PAS', '2/1/2017', '5', 'SW', 'LINCO', 'BUS LANE VIOLATION']\n",
      "['4007306795', 'HLG4926', 'NY', 'PAS', '5/30/2017', '5', 'SUBN', 'TOYOT', 'BUS LANE VIOLATION']\n",
      "['4007124590', 'T715907C', 'NY', 'OMT', '4/3/2017', '5', 'SUBN', 'TOYOT', 'BUS LANE VIOLATION']\n",
      "['5096061966', 'HRC9475', 'NY', 'PAS', '4/18/2017', '7', 'SUBN', 'CADIL', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5094070400', 'DYP8042', 'NY', 'PAS', '10/26/2016', '7', 'SUBN', 'CHEVR', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5094906770', 'G30ESY', 'NJ', 'PAS', '1/1/2017', '7', 'WAGO', 'CHRYS', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5093319363', 'GGT8868', 'NY', 'PAS', '9/6/2016', '7', 'SUBN', 'CHRYS', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5092638849', '107DDD', 'NY', 'SPO', '7/20/2016', '7', 'SUBN', 'DODGE', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5093455337', 'ENQT55', 'FL', 'PAS', '9/12/2016', '7', 'VN', 'DODGE', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5094961366', 'GSU4156', 'OH', 'PAS', '1/7/2017', '7', '4S', 'DODGE', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5092906534', 'V84FXV', 'NJ', 'PAS', '8/7/2016', '7', 'WAGO', 'FIR', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5093620865', 'AD80228', 'AZ', 'PAS', '9/24/2016', '7', 'TK', 'FORD', 'FAILURE TO STOP AT RED LIGHT']\n",
      "['5092733548', 'EVX4041', 'NY', 'PAS', '7/26/2016', '7', 'SUBN', 'FORD', 'FAILURE TO STOP AT RED LIGHT']\n"
     ]
    }
   ],
   "source": [
    "with data_iter as data:\n",
    "    for row in data:\n",
    "        print(row)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
